{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "26f7194c",
   "metadata": {},
   "source": [
    "# CNN模型实现与MNIST数据集训练\n",
    "\n",
    "本notebook演示了如何使用自定义CNN模型在MNIST数据集上进行手写数字识别。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f629408e",
   "metadata": {},
   "source": [
    "由于是手动实现的CNN模型，所以训练速度很慢，使用较少的数据集，并且模型的能力特别差。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "949acf31",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "正在加载MNIST数据集...\n",
      "数据加载完成：800 个训练样本，200 个测试样本\n",
      "开始训练模型...\n",
      "Epoch 1/3 训练中...\n",
      "数据加载完成：800 个训练样本，200 个测试样本\n",
      "开始训练模型...\n",
      "Epoch 1/3 训练中...\n",
      "Batch 1/26, Loss: 2.3026\n",
      "Batch 1/26, Loss: 2.3026\n",
      "Batch 6/26, Loss: 2.3027\n",
      "Batch 6/26, Loss: 2.3027\n",
      "Batch 11/26, Loss: 2.3023\n",
      "Batch 11/26, Loss: 2.3023\n",
      "Batch 16/26, Loss: 2.3028\n",
      "Batch 16/26, Loss: 2.3028\n",
      "Batch 21/26, Loss: 2.3027\n",
      "Batch 21/26, Loss: 2.3027\n",
      "Epoch 1/3, Loss: 2.3026, Accuracy: 0.1000\n",
      "Epoch 2/3 训练中...\n",
      "Epoch 1/3, Loss: 2.3026, Accuracy: 0.1000\n",
      "Epoch 2/3 训练中...\n",
      "Batch 1/26, Loss: 2.3017\n",
      "Batch 1/26, Loss: 2.3017\n",
      "Batch 6/26, Loss: 2.3027\n",
      "Batch 6/26, Loss: 2.3027\n",
      "Batch 11/26, Loss: 2.3024\n",
      "Batch 11/26, Loss: 2.3024\n",
      "Batch 16/26, Loss: 2.3014\n",
      "Batch 16/26, Loss: 2.3014\n",
      "Batch 21/26, Loss: 2.3012\n",
      "Batch 21/26, Loss: 2.3012\n",
      "Epoch 2/3, Loss: 2.3022, Accuracy: 0.1000\n",
      "Epoch 3/3 训练中...\n",
      "Epoch 2/3, Loss: 2.3022, Accuracy: 0.1000\n",
      "Epoch 3/3 训练中...\n",
      "Batch 1/26, Loss: 2.3038\n",
      "Batch 1/26, Loss: 2.3038\n",
      "Batch 6/26, Loss: 2.3005\n",
      "Batch 6/26, Loss: 2.3005\n",
      "Batch 11/26, Loss: 2.3015\n",
      "Batch 11/26, Loss: 2.3015\n",
      "Batch 16/26, Loss: 2.3023\n",
      "Batch 16/26, Loss: 2.3023\n",
      "Batch 21/26, Loss: 2.3031\n",
      "Batch 21/26, Loss: 2.3031\n",
      "Epoch 3/3, Loss: 2.3019, Accuracy: 0.1000\n",
      "训练完成！总时间: 534.98 秒\n",
      "Epoch 3/3, Loss: 2.3019, Accuracy: 0.1000\n",
      "训练完成！总时间: 534.98 秒\n",
      "最终测试准确率: 0.1000\n",
      "最终测试准确率: 0.1000\n"
     ]
    },
    {
     "data": {
      "image/png": "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h/qW9r37rlS1qm+XPgAAACA2JKUAAECiiIiQunWTvvpKuuoqad06qXx5afZsr0cGAACAlIikFAAASFS1aklr1kgVK0oHDkh33im99JJ07pzXIwMAAEBKQlIKAAAkukKFpG++kTp29DU9f/55qWlT6dAhr0cGAACAlIKkFAAASBLp00ujRkljxkjp0kkzZviqpzZu9HpkAAAASAlISgEAgCTVoYO0eLFUsKC0ebMvMTV1qtejAgAAgNdISgEAgCQXGenrM2X9po4dk5o3l55+WjpzxuuRAQAAwCskpQAAQLLInVuaM0fq3t13ecgQqV49ae9er0cGAAAAL5CUAgAAySZNGmnwYGnKFClTJmnePKl8eWn1aq9HBgAAgORGUgoAACQ7W763YoV03XXSjh1StWrSO+94PSoAAAAkJ5JSAADAE6VKSStXSo0bSydPSg89JD3yiO93AAAAhD6SUgAAwDPZskkffyz17y9FREijR0u33ir9/rvXIwMAAEBSIykFAAA8lSqV1LOnNHOmlD27r3qqXDlpwQKvRwYAAICkRFIKAACkCHfc4Wt4XqaMb0e+OnWkYcOkqCivRwYAAICkQFIKAACkGMWKSUuWSA88IJ09Kz31lNSqlXTsmNcjAwAAQGIjKQUAAFKUjBmlCROkESOkNGmkyZOlypWln3/2emQAAABITCSlAABAimNNz7t08fWVyptX2rBBioyUPv/c65EBAAAgsZCUAgAAKdYtt0jffuv7eeiQ1LCh1KePdO6c1yMDAADA5SIpBQAAUrR8+aR583yVU+aFF3zJqQMHvB4ZAAAALgdJKQAAkOKlS+frMfXuu1KGDNLMmVKFCtJ333k9MgAAAFwqklIAACBo2K58S5dKV18tbd3qa4A+aZLXowIAAMClICkFAACCStmy0po1Ur160okTUuvW0hNPSKdPez0yAAAAXAySUgAAIOjkyCF98YXUs6fv8muvSbfdJu3e7fXIAAAAkFAkpQAAQFBKnVrq31/65BMpSxZp0SKpfHlp2TKvRwYAAICEICkFAACCWuPG0qpVUsmS0q5dUo0a0ptvej0qAAAAxIekFAAACHolSkjLl0v33OPrLbV+vdcjAgAAQHzSxHsLAACAIGBL+KZMkd59V7r3Xq9HAwAAgPiQlAIAACEjIkJq08brUQAAACAhWL4HAAAAAACAZEdSCgAA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",
      "text/plain": [
       "<Figure size 1200x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABawAAAExCAYAAACQ6eWzAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvc2/+5QAAAAlwSFlzAAAPYQAAD2EBqD+naQAAKN1JREFUeJzt3QmUlNWZP+Db0EArqCAiURxXjOgBg+LCRJTFJUYYkBijiUPcwD3iyrgLQWNQiUY0jltmXGLUjDIKjHEigoCMCy4cERQcQaOgqKOIKHv9z1fnwF9c6jZ0UX2r+nnOKaHr/vp+t4F6rXq/rSqXy+UCAAAAAADUs0b1vQAAAAAAAMhoWAMAAAAAkAQNawAAAAAAkqBhDQAAAABAEjSsAQAAAABIgoY1AAAAAABJ0LAGAAAAACAJGtYAAAAAACRBw7pMLV26NCxbtizkcrl1ns++zsaWL1++9rmv/r42PvjggzBt2rSirRVoeNQoIGVqFJAyNQpImRpFKVSXZCtssL322iv/gm3atOna5/r37x8WL14c7rrrru/8vhtuuCGcc845YeXKlWH//fcPhxxySLjmmmvC/Pnzw8yZM7+R79Wr19ptjBs3Llx22WX5LEAhahSQMjUKSJkaBaRMjaI+aVgn7oEHHsj/mr14x44dGy699NLQr1+/0LVr13DzzTeHZs2ahWOOOSa0bds2jBo1au0ercaNG+e/r7q6OlxyySXh5JNPDnPnzg2HH354OO+888Iee+yRH8/2ir3yyivhs88+yxedmpqa/La+WpCK6c477wz33XdfmDhx4kaZHygtNQpImRoFpEyNAlKmRlGfNKwTt9tuu4Xf/OY3YccddwzDhg0LDz/8cOjRo8c6mTlz5oQf/ehH+d9XVVWFTTbZZJ3xo48+Ouy5557h008/DbNnzw5777332hfovHnzwk477RSaNGkSDjvssPDTn/40X2w2hqeffjqcddZZ+eIGVAY1CkiZGgWkTI0CUqZGUZ9cw7oMtG/fPgwaNCgcfPDB4dBDD80/l52WsdVWW+Uf06dPz++lWvP1P/7jP67z/dleq6zQZKdiZAVkzYu1W7duYdGiRWtz2V6sbA/ZxjBmzJjQu3fvtXvSgMqhRgEpU6OAlKlRQMrUKOqLhnXCstMpsgvU/+xnPwtPPPFE6NChw9qxbA/Uxx9/HJ577rn8nqrsBf/222+Ha6+9NnzxxRdrc9lpG9mLMisiX9WoUaPwzDPPhObNm699bk3x2BgmTZoUHnroodC3b9+Ntg2gtNQoIGVqFJAyNQpImRpFfdOwTli21yrbw5Rd/yc77eLqq6/OXwMoe2QvuEw29uabb+Yvhv/hhx/mX/hZ8fjqxev33XffcMABB4QpU6asfT6bI7s+UPZrbQ0dOjS0bNlyg36WESNGhCOOOGKDvhdIkxoFpEyNAlKmRgEpU6OobxrWCcuu3fP555/n76x65JFH5u+U+uKLL+bHvnrdnS+//DJfFHbYYYdvzLHpppuG+++/P1x44YWhU6dO64xlBWJ9nHLKKRt8cfqscAGVRY0CUqZGASlTo4CUqVHUNzddTFh2SsRXT5HIZHutjj322NCiRYt1CkRWTL7rFIrsxTlkyJD83q/Vq1fnn/voo4/CFltssTaz5vlCtt122/wDIKNGASlTo4CUqVFAytQo6pvdDGXmkEMOCffdd1/+ekJrvPvuu+u8cL86tsa//du/hYMOOigsXbo0//WCBQvW+Z7sQvgAdaVGASlTo4CUqVFAytQoSskR1mUqOy1jjeeff36dC+BnF8b/ujvuuCPsueeeoUuXLmGTTTYJ48ePD9tvv31o3bp1uPXWW/OnY6xatepbvxdgfalRQMrUKCBlahSQMjWKUnCEdRkXiOwaQStWrAiPPPJIOPTQQ/PPZ3up9tlnn3WyL7zwQv6urNl1g7ICcdxxx4W//e1v+T1cDz/8cDjttNPyRSPbq6VAAMWgRgEpU6OAlKlRQMrUKEpBw7pMtWnTJsybNy/84Q9/CE2bNs3ftfWwww4LW2+9df50i6/K7uaa3RG1Y8eO+a+zi95nd3zdbLPNwoknnpjf27Vmz9hFF130nducP39+eOWVVzbyTwZUAjUKSJkaBaRMjQJSpkZRChrWiXvnnXfC//zP/4TXX389tGrVau3z2Z6ns88+O9x1113hz3/+c35P1k9+8pPQs2fPcMstt6zNZadaPProo2Hw4MH5r5csWRKGDRsWTjrppDBgwIBwwQUXhHPPPTf87//+79rvyU7F+Da33357vhABrKFGASlTo4CUqVFAytQo6lWOpP33f/93rnnz5rlevXrlFixYkH/u2WefzW2//fa53XffPTd9+vR18tlY69atc9dff33+62HDhuXat2+fW716df7Rv3//XMuWLXPvvfdefnzp0qW5XXfdNTd48OD811dccUV+3j333LPkPytQftQoIGVqFJAyNQpImRpFfarK/lO/LXM2xLhx4/LXCcpOv/i6bO9Uu3bt8heuz7z//vvhe9/7Xv6aQNdee234wQ9+EPr27bs2P2vWrNC+ffvQpEmTMGLEiPDqq6+Gc8455xvXHgKoLTUKSJkaBaRMjQJSpkZRChrWAAAAAAAkwTWsAQAAAABIgoZ1A/bGG2+EL774or6XAfCt1CggZWoUkDI1CkiZGkWMhnWZeu+998Ljjz+ev8vqhnjttddChw4d8nd0BSg2NQpImRoFpEyNAlKmRlEKrmGduOXLl4fVq1evvWD9Gvfcc08YPHhw+Oijj0Ljxo3XPr9y5cqwdOnS0KJFi+jce++9d/7C9s8999xGWTtQ+dQoIGVqFJAyNQpImRpFfXKEdeIeeOCBsOWWW4bWrVuHrbbaau3j1FNPzZ8+0bZt23Wezx5dunRZZ45BgwaFqqqqbzxefvnl8Pzzz3/r2CmnnFLUn2POnDnhkEMOCZtuumm+eP3zP/9z+Pzzz4u6DaD0KqVGZW+sTj/99PzPsfPOO4eHHnqoqPMD9aNSatQad955Z+jRo8dGmRsovUqpUT7rQWWqlBq1xpdffpn/rHfWWWdtlPkpruoiz0eR/fKXv8w/vu74448P2cHx2Z6tmE022SR07NgxTJ48+Rtjq1atWmeP2Jo9XV/fg1YXWSHr3bt3fu/ZDTfcED7++OPw61//Or+N7IMXUL4qoUZlsjdFY8aMCb///e/zRxGceOKJYfvttw9du3Yt6naA0qqUGpV5+umn8x+w1CWoHJVQo3zWg8pVCTXqq4YNGxYWL14chg8fvlHmp7gcYV1Ge7a+uscpKwz33nvvN/ZEvf/++9965GBWBFq2bBlGjx4dXn311fzvs8eLL74YfvCDH4Q333xz7XPZXrHq6uLty7jvvvvya3jmmWfye+IuueSScO6554b/+I//KNo2gPpVzjUqu+FHVqduu+22/BuyE044IZx//vneyEAFKecalcl2qGUNoT322KOo8wJpKOca5bMeVL5yrlFrzJgxI/zud78LV199dWjVqlXR56f4HGFdJrK9Upnvuij9zJkz882VZs2afWPsxhtvzO+5ykybNi1/SsbAgQPDiBEj8oXjnXfeCVtsscXafHZaRjH1798/9OrVK1981shOKVmzJqD8lXONeuqpp/Lrz2rVGkceeWS4/vrrv3WvP1B+yrlGZSZNmpS/VFE298SJE4s+P1C/yrlG+awHla+ca1QmOxo826HWqVOn/LYpD46wLhNrGiaffvrptz7WXCMs26v1ddm1xLK7sGZ3cr3lllvyzZlx48aFK664In8R/cx2220XXcPQoUPXeSNSW23atAnt27df57nsjrI//OEP13suIE3lXKOy7WZ3qc5OZV0ju7ZZdo2zbAwof+VcozLZh7ojjjhig74XSF851yif9aDylXONyvzxj38MU6dOzderAQMG5C8NsmjRog2ai9JxhHWZyW4KtiGuuuqq/LUPsyMGsz1L2QXus+bME088kX/RrtljFrvGa3bUYV1l68iKVPZGBqgs5Vijssb019/8rLmz9Ycffpi/ljVQGcqxRmUaNXKMCTQE5VqjvspnPahc5VijskuSZM3xzIIFC8KyZcvyZ61llzN67rnn8jeVJE0a1mVm7ty53/r8s88+G37+859/5/c98sgj+SJxxhln5Bswl112Wf75v//972GHHXao1ba33Xbb/KOuN+XITgE5/PDD8w+gspRjjcpOXfv6ZT/WHB2QNbOBylGONQpoOMq9RvmsB5WtHGtUdjT3/Pnzw+DBg/OXJ8lk184+4IADwsiRI/PXtCZNGtZlZqeddlrvNw0ffPBBviGTFYdddtklf4fWNRfDnz17dth6663XuTj+ihUr8qdmZEcVfvUU+WLIbsDxf//3f2HChAlFnRdIQznWqGz+d999d53nsjvcZ5o3b17n+YF0lGONAhqOcq9RPutBZSvHGpVtIzNkyJC1z3Xp0iXss88++SO9SZeGdcKyC8NnL/Campq1zy1evPhbs5MnT157bcPVq1fnT3PITqvIrtNz6KGHRre1zTbbfOO5OXPmfON6ZHWR3Un2jjvuCI899lho165d0eYF6kel1KjsztTZXAsXLsy/YVqz1z3jaEgoX5VSo4DKVGk1ymc9qCyVUqOyA5CyhvnXP9dl62vatGmd52cjypGsBQsW5LK/og157L///vk5Vq5cmfviiy/yv37dqFGjcltuuWXugw8+yC1ZsmTt88uWLcstXrw4t2rVqqL9LBMmTMg1bdo0d/HFFxdtTqB+VUqNWr58ea5t27a5Cy+8MP/16tWrcz/+8Y9z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",
      "text/plain": [
       "<Figure size 1500x300 with 5 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import numpy as np\n",
    "from CNNModel import CNN\n",
    "import matplotlib.pyplot as plt\n",
    "import time\n",
    "from sklearn.datasets import fetch_openml\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.preprocessing import LabelEncoder\n",
    "\n",
    "# 设置中文显示\n",
    "import matplotlib as mpl\n",
    "\n",
    "plt.rcParams[\"font.sans-serif\"] = [\"SimHei\"]  # 设置中文字体为黑体\n",
    "plt.rcParams[\"axes.unicode_minus\"] = False  # 解决负号显示问题\n",
    "\n",
    "\n",
    "def load_mnist_dataset(sample_size=1000):\n",
    "    \"\"\"\n",
    "    加载MNIST数据集\n",
    "\n",
    "    参数:\n",
    "    sample_size -- 如果提供，限制数据集大小为指定值\n",
    "\n",
    "    返回:\n",
    "    X_train, X_test, y_train, y_test -- 训练集和测试集\n",
    "    \"\"\"\n",
    "    print(\"正在加载MNIST数据集...\")\n",
    "    # 从OpenML加载MNIST数据集\n",
    "    X, y = fetch_openml(\"mnist_784\", version=1, return_X_y=True, as_frame=False)\n",
    "\n",
    "    # 将数据限制到指定大小\n",
    "    if sample_size and sample_size < len(X):\n",
    "        X = X[:sample_size]\n",
    "        y = y[:sample_size]\n",
    "\n",
    "    # 标准化数据\n",
    "    X = X / 255.0\n",
    "\n",
    "    # 将标签从字符转为整数\n",
    "    label_encoder = LabelEncoder()\n",
    "    y = label_encoder.fit_transform(y)\n",
    "\n",
    "    # 重塑数据为 (样本数, 通道数, 高度, 宽度)\n",
    "    X = X.reshape(-1, 1, 28, 28)\n",
    "\n",
    "    # 分割为训练集和测试集\n",
    "    X_train, X_test, y_train, y_test = train_test_split(\n",
    "        X, y, test_size=0.2, random_state=42\n",
    "    )\n",
    "\n",
    "    print(f\"数据加载完成：{X_train.shape[0]} 个训练样本，{X_test.shape[0]} 个测试样本\")\n",
    "    return X_train, X_test, y_train, y_test\n",
    "\n",
    "\n",
    "def evaluate_model(model, X_test, y_test, batch_size=32):\n",
    "    \"\"\"\n",
    "    评估模型在测试集上的表现\n",
    "\n",
    "    参数:\n",
    "    model -- CNN模型实例\n",
    "    X_test -- 测试数据\n",
    "    y_test -- 测试标签\n",
    "    batch_size -- 批量大小\n",
    "\n",
    "    返回:\n",
    "    accuracy -- 准确率\n",
    "    \"\"\"\n",
    "    num_samples = X_test.shape[0]\n",
    "    correct = 0\n",
    "\n",
    "    # 分批进行预测以处理大型数据集\n",
    "    for i in range(0, num_samples, batch_size):\n",
    "        end = min(i + batch_size, num_samples)\n",
    "        X_batch = X_test[i:end]\n",
    "        y_batch = y_test[i:end]\n",
    "\n",
    "        # 获取预测结果\n",
    "        predictions = model.predict(X_batch)\n",
    "\n",
    "        # 计算准确的预测数量\n",
    "        correct += np.sum(predictions == y_batch)\n",
    "\n",
    "    # 计算准确率\n",
    "    accuracy = correct / num_samples\n",
    "    return accuracy\n",
    "\n",
    "\n",
    "def visualize_results(losses, accuracies, save_path=None):\n",
    "    \"\"\"\n",
    "    可视化训练结果\n",
    "\n",
    "    参数:\n",
    "    losses -- 每个epoch的损失列表\n",
    "    accuracies -- 每个epoch的准确率列表\n",
    "    save_path -- 保存图像的路径，如果是None则显示图像\n",
    "    \"\"\"\n",
    "    epochs = range(1, len(losses) + 1)\n",
    "\n",
    "    plt.figure(figsize=(12, 5))\n",
    "\n",
    "    # 绘制损失曲线\n",
    "    plt.subplot(1, 2, 1)\n",
    "    plt.plot(epochs, losses, \"b-\", label=\"训练损失\")\n",
    "    plt.title(\"训练损失\")\n",
    "    plt.xlabel(\"Epochs\")\n",
    "    plt.ylabel(\"损失\")\n",
    "    plt.legend()\n",
    "\n",
    "    # 绘制准确率曲线\n",
    "    plt.subplot(1, 2, 2)\n",
    "    plt.plot(epochs, accuracies, \"r-\", label=\"测试准确率\")\n",
    "    plt.title(\"测试准确率\")\n",
    "    plt.xlabel(\"Epochs\")\n",
    "    plt.ylabel(\"准确率\")\n",
    "    plt.legend()\n",
    "\n",
    "    plt.tight_layout()\n",
    "\n",
    "    if save_path:\n",
    "        plt.savefig(save_path)\n",
    "    else:\n",
    "        plt.show()\n",
    "\n",
    "\n",
    "def visualize_predictions(model, X_test, y_test, num_samples=5):\n",
    "    \"\"\"\n",
    "    可视化预测结果\n",
    "\n",
    "    参数:\n",
    "    model -- CNN模型实例\n",
    "    X_test -- 测试数据\n",
    "    y_test -- 测试标签\n",
    "    num_samples -- 显示样本数量\n",
    "    \"\"\"\n",
    "    # 随机选择样本\n",
    "    indices = np.random.choice(len(X_test), num_samples, replace=False)\n",
    "\n",
    "    plt.figure(figsize=(15, 3))\n",
    "\n",
    "    for i, idx in enumerate(indices):\n",
    "        # 获取样本\n",
    "        image = X_test[idx, 0]  # 取出第一个通道\n",
    "        true_label = y_test[idx]\n",
    "\n",
    "        # 获取预测\n",
    "        prediction = model.predict(X_test[idx : idx + 1])[0]\n",
    "\n",
    "        # 显示图像和标签\n",
    "        plt.subplot(1, num_samples, i + 1)\n",
    "        plt.imshow(image, cmap=\"gray\")\n",
    "        plt.title(f\"预测: {prediction}\\n真实: {true_label}\")\n",
    "        plt.axis(\"off\")\n",
    "\n",
    "    plt.tight_layout()\n",
    "    plt.show()\n",
    "\n",
    "\n",
    "def main():\n",
    "    # 加载数据集（使用合适大小的样本以平衡速度和效果）\n",
    "    X_train, X_test, y_train, y_test = load_mnist_dataset(sample_size=1000)\n",
    "\n",
    "    # 设置训练参数\n",
    "    batch_size = 32  # 更小的批量大小\n",
    "    epochs = 3  # 减少轮次\n",
    "    learning_rate = 0.01\n",
    "\n",
    "    # 初始化CNN模型\n",
    "    input_shape = (1, 28, 28)  # 单通道，28x28像素\n",
    "    num_classes = 10  # MNIST有10个类别（数字0-9）\n",
    "    model = CNN(input_shape=input_shape, num_classes=num_classes)\n",
    "\n",
    "    print(\"开始训练模型...\")\n",
    "    start_time = time.time()\n",
    "\n",
    "    # 训练模型并记录损失和准确率\n",
    "    losses = []\n",
    "    accuracies = []\n",
    "\n",
    "    for epoch in range(epochs):\n",
    "        # 训练一个epoch\n",
    "        print(f\"Epoch {epoch+1}/{epochs} 训练中...\")\n",
    "        num_samples = X_train.shape[0]\n",
    "        total_loss = 0\n",
    "\n",
    "        # 打乱数据\n",
    "        indices = np.random.permutation(num_samples)\n",
    "        X_shuffled = X_train[indices]\n",
    "        y_shuffled = y_train[indices]\n",
    "\n",
    "        # 批量训练\n",
    "        for i in range(0, num_samples, batch_size):\n",
    "            end = min(i + batch_size, num_samples)\n",
    "            X_batch = X_shuffled[i:end]\n",
    "            y_batch = y_shuffled[i:end]\n",
    "\n",
    "            # 前向传播计算损失\n",
    "            loss = model.forward(X_batch, y_batch)\n",
    "            total_loss += loss\n",
    "\n",
    "            # 反向传播更新参数\n",
    "            model.backward(learning_rate)\n",
    "\n",
    "            # 只打印部分进度以减少输出\n",
    "            if (i // batch_size) % 5 == 0:\n",
    "                print(\n",
    "                    f\"Batch {i//batch_size+1}/{num_samples//batch_size+1}, Loss: {loss:.4f}\"\n",
    "                )\n",
    "\n",
    "        # 计算平均损失\n",
    "        avg_loss = total_loss / (num_samples // batch_size)\n",
    "        losses.append(avg_loss)\n",
    "\n",
    "        # 评估模型\n",
    "        accuracy = evaluate_model(model, X_test, y_test, batch_size)\n",
    "        accuracies.append(accuracy)\n",
    "\n",
    "        print(\n",
    "            f\"Epoch {epoch+1}/{epochs}, Loss: {avg_loss:.4f}, Accuracy: {accuracy:.4f}\"\n",
    "        )\n",
    "\n",
    "    # 计算总训练时间\n",
    "    training_time = time.time() - start_time\n",
    "    print(f\"训练完成！总时间: {training_time:.2f} 秒\")\n",
    "\n",
    "    # 最终评估\n",
    "    final_accuracy = evaluate_model(model, X_test, y_test, batch_size)\n",
    "    print(f\"最终测试准确率: {final_accuracy:.4f}\")\n",
    "\n",
    "    # 可视化训练结果\n",
    "    visualize_results(losses, accuracies)\n",
    "\n",
    "    # 可视化预测结果\n",
    "    visualize_predictions(model, X_test, y_test)\n",
    "\n",
    "\n",
    "if __name__ == \"__main__\":\n",
    "    main()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "61439c72",
   "metadata": {},
   "source": [
    "## 训练结果说明\n",
    "\n",
    "上面的训练过程使用了较小的数据集(500个样本)以加快训练速度，同时保持一定的识别效果。\n",
    "\n",
    "您可以通过调整以下参数来优化训练:\n",
    "- `sample_size`: 增加可提高准确率，但会减慢训练速度\n",
    "- `batch_size`: 较小的批量大小通常需要更多迭代但可能更稳定\n",
    "- `epochs`: 增加训练轮次可提高模型性能，但会增加训练时间\n",
    "- `learning_rate`: 学习率影响模型收敛速度和稳定性"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "beb71522",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 可选：尝试不同的超参数\n",
    "# 以下是一个示例，展示如何使用不同的超参数进行训练\n",
    "\n",
    "\n",
    "def train_with_params(sample_size=500, batch_size=32, epochs=3, learning_rate=0.01):\n",
    "    print(\n",
    "        f\"使用参数: 样本数={sample_size}, 批量大小={batch_size}, 轮次={epochs}, 学习率={learning_rate}\"\n",
    "    )\n",
    "\n",
    "    # 加载数据集\n",
    "    X_train, X_test, y_train, y_test = load_mnist_dataset(sample_size=sample_size)\n",
    "\n",
    "    # 初始化模型\n",
    "    input_shape = (1, 28, 28)\n",
    "    num_classes = 10\n",
    "    model = CNN(input_shape=input_shape, num_classes=num_classes)\n",
    "\n",
    "    # 记录训练开始时间\n",
    "    start_time = time.time()\n",
    "\n",
    "    # 训练模型(简化版，不打印每个batch)\n",
    "    losses = []\n",
    "    accuracies = []\n",
    "\n",
    "    for epoch in range(epochs):\n",
    "        # 打乱数据\n",
    "        indices = np.random.permutation(X_train.shape[0])\n",
    "        X_shuffled = X_train[indices]\n",
    "        y_shuffled = y_train[indices]\n",
    "\n",
    "        epoch_loss = 0\n",
    "        batches = 0\n",
    "\n",
    "        # 批量训练\n",
    "        for i in range(0, X_train.shape[0], batch_size):\n",
    "            end = min(i + batch_size, X_train.shape[0])\n",
    "            X_batch = X_shuffled[i:end]\n",
    "            y_batch = y_shuffled[i:end]\n",
    "\n",
    "            loss = model.forward(X_batch, y_batch)\n",
    "            model.backward(learning_rate)\n",
    "\n",
    "            epoch_loss += loss\n",
    "            batches += 1\n",
    "\n",
    "        # 记录平均损失\n",
    "        avg_loss = epoch_loss / batches\n",
    "        losses.append(avg_loss)\n",
    "\n",
    "        # 评估当前模型\n",
    "        accuracy = evaluate_model(model, X_test, y_test, batch_size)\n",
    "        accuracies.append(accuracy)\n",
    "\n",
    "        print(\n",
    "            f\"Epoch {epoch+1}/{epochs}, Loss: {avg_loss:.4f}, Accuracy: {accuracy:.4f}\"\n",
    "        )\n",
    "\n",
    "    # 计算训练时间\n",
    "    total_time = time.time() - start_time\n",
    "    print(f\"训练完成! 总时间: {total_time:.2f}秒, 最终准确率: {accuracies[-1]:.4f}\")\n",
    "\n",
    "    # 返回最终模型和训练历史\n",
    "    return model, losses, accuracies\n",
    "\n",
    "\n",
    "# 取消注释下面的行来运行测试\n",
    "# model, losses, accuracies = train_with_params(sample_size=300, batch_size=16, epochs=2)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.16"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
